/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.metaaps.eoraster;

import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.lang.reflect.Array;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import ncsa.hdf.hdf5lib.exceptions.HDF5Exception;
import ncsa.hdf.object.Attribute;
import ncsa.hdf.object.h5.H5File;
import ncsa.hdf.object.h5.H5ScalarDS;

/**
 * Class to read Cosmo Skymed Images in hdf5 format.
 * To be able to read it the HDF5 DLLs or SOs files should be in the classpath
 * @author thoorfr
 */
public class CosmoSkymedImage extends SARImageReader {

    private H5File h5file;
    private H5ScalarDS imagedata;
    private short[] preloadedData;
    private Rectangle bounds;
    private int[] preloadedInterval = new int[]{0, 0};
    private long[] stride;
    private long[] dims;
    private long[] starts;
    private boolean complex = false;

    public int getNBand() {
        return 1;
    }

    public String getFormat() {
        return "CosmoSkymedImage";
    }

    public int getNumberOfBytes() {
        return 2;
    }

    public int getType(boolean oneBand) {
        return BufferedImage.TYPE_USHORT_GRAY;
    }

    public String getAccessRights() {
        return "r";
    }

    public String[] getFilesList() {
        return new String[]{h5file.getFilePath()};
    }

    public boolean initialise() {
        try {
            imagedata = (H5ScalarDS) h5file.get("S01/SBI");
            if (imagedata == null) {
                imagedata = (H5ScalarDS) h5file.get("MBI");
                if (imagedata == null) {
                    return false;
                }
            }
            extractQuickLook();
            List metadata = imagedata.getMetadata();
            metadata.addAll(h5file.get("/").getMetadata());
            metadata.addAll(h5file.get("S01").getMetadata());
            xSize = (int) imagedata.getDims()[1];
            ySize = (int) imagedata.getDims()[0];
            stride = imagedata.getStride();
            dims = imagedata.getSelectedDims();
            starts = imagedata.getStartDims();
            setMetadata(WIDTH, xSize);
            setMetadata(HEIGHT, ySize);
            bounds = new Rectangle(0, 0, xSize, ySize);
            gcps = new ArrayList<GCP>();
            for (Object o : metadata) {
                Attribute a = (Attribute) o;
                System.out.println(a.getName() + "=" + a.getValue().toString());
                if (a.getName().equals("Bottom Left Geodetic Coordinates")) {
                    double[] val = (double[]) a.getValue();
                    GCP gcp = new GCP();
                    gcp.setXpix(0);
                    gcp.setYpix(ySize);
                    gcp.setXgeo(val[1]);
                    gcp.setYgeo(val[0]);
                    gcps.add(gcp);
                } else if (a.getName().equals("Bottom Right Geodetic Coordinates")) {
                    double[] val = (double[]) a.getValue();
                    GCP gcp = new GCP();
                    gcp.setXpix(xSize);
                    gcp.setYpix(ySize);
                    gcp.setXgeo(val[1]);
                    gcp.setYgeo(val[0]);
                    gcps.add(gcp);
                } else if (a.getName().equals("Top Left Geodetic Coordinates")) {
                    double[] val = (double[]) a.getValue();
                    GCP gcp = new GCP();
                    gcp.setXpix(0);
                    gcp.setYpix(0);
                    gcp.setXgeo(val[1]);
                    gcp.setYgeo(val[0]);
                    gcps.add(gcp);
                } else if (a.getName().equals("Top Right Geodetic Coordinates")) {
                    double[] val = (double[]) a.getValue();
                    GCP gcp = new GCP();
                    gcp.setXpix(xSize);
                    gcp.setYpix(0);
                    gcp.setXgeo(val[1]);
                    gcp.setYgeo(val[0]);
                    gcps.add(gcp);
                } else if (a.getName().equals("Scene Sensing Start UTC")) {
                    String[] val = (String[]) a.getValue();
                    setMetadata(TIMESTAMP_START, Timestamp.valueOf(val[0]));
                } else if (a.getName().equals("Scene Sensing Stop UTC")) {
                    String[] val = (String[]) a.getValue();
                    setMetadata(TIMESTAMP_STOP, Timestamp.valueOf(val[0]));
                } else if (a.getName().equals("Equivalent Number of Looks")) {
                    double[] val = (double[]) a.getValue();
                    setMetadata(ENL, val[0]);
                } else if (a.getName().equals("Column Spacing")) {
                    double[] val = (double[]) a.getValue();
                    setMetadata(RANGE_SPACING, val[0]);
                } else if (a.getName().equals("Far Incidence Angle")) {
                    double[] val = (double[]) a.getValue();
                    setMetadata(INCIDENCE_FAR, val[0]);
                } else if (a.getName().equals("Near Incidence Angle")) {
                    double[] val = (double[]) a.getValue();
                    setMetadata(INCIDENCE_NEAR, val[0]);
                } else if (a.getName().equals("Line Spacing")) {
                    double[] val = (double[]) a.getValue();
                    setMetadata(AZIMUTH_SPACING, val[0]);
                } else if (a.getName().equals("Look Side")) {
                    String[] val = (String[]) a.getValue();
                    setMetadata(LOOK_DIRECTION, val[0]);
                } else if (a.getName().equals("Orbit Direction")) {
                    String[] val = (String[]) a.getValue();
                    setMetadata(ORBIT_DIRECTION, val[0]);
                } else if (a.getName().equals("Processing Centre")) {
                    String[] val = (String[]) a.getValue();
                    setMetadata(PROCESSOR, val[0]);
                } else if (a.getName().equals("Radar Wavelength")) {
                    double[] val = (double[]) a.getValue();
                    setMetadata(RADAR_WAVELENGTH, val[0]);
                } else if (a.getName().equals("Product Type")) {
                    String[] val = (String[]) a.getValue();
                    setMetadata(TYPE, val[0]);
                } else if (a.getName().equals("Satellite ID")) {
                    String[] val = (String[]) a.getValue();
                    setMetadata(SATELLITE, val[0]);
                } else if (a.getName().equals("Satellite Height")) {
                    double[] val = (double[]) a.getValue();
                    setMetadata(SATELLITE_ALTITUDE, val[0]);
                } else if (a.getName().equals("Polarisation")) {
                    String[] val = (String[]) a.getValue();
                    setMetadata(POLARISATION, val[0]);
                } else if (a.getName().equals("Multi-Beam ID")) {
                    String[] val = (String[]) a.getValue();
                    setMetadata(BEAM, val[0]);
                }


            }
            if (("" + getMetadata(TYPE)).startsWith("SCS")) {
                complex = true;
            }
            geotransform = GeoTransformFactory.createFromGcps(gcps, "EPSG:4326");
            System.out.println(imagedata.getWidth() + "--" + imagedata.getHeight());

        } catch (Exception ex) {
            //Logger.getLogger(CosmoSkymedImage.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
        return true;
    }

    public void setFile(File imageFile) {
        h5file = new H5File(imageFile.getAbsolutePath(), H5File.READ);
    }

    public int[] readTile(int x, int y, int width, int height) {
        Rectangle rect = new Rectangle(x, y, width, height);
        rect = rect.intersection(bounds);
        int[] tile = new int[height * width];
        if (rect.isEmpty()) {
            return tile;
        }
        if (rect.y != preloadedInterval[0] || rect.y + rect.height != preloadedInterval[1]) {
            preloadLineTile(rect.y, rect.height);
        }
        if (complex) {
            int yOffset = 2 * xSize;
            int xinit = rect.x - x;
            int yinit = rect.y - y;
            try {
                for (int i = 0; i < rect.height; i++) {
                    for (int j = 0; j < rect.width; j++) {
                        int temp = i * yOffset + 2 * j + 2 * rect.x;
                        long temp1 = preloadedData[temp];
                        long temp2 = preloadedData[temp + 1];
                        tile[(i + yinit) * width + j + xinit] = (int) Math.sqrt(temp1 * temp1 + temp2 * temp2);
                    }
                }
            } catch (Exception e) {
                System.out.println(e);
            }
        } else {
             int yOffset = xSize;
            int xinit = rect.x - x;
            int yinit = rect.y - y;
            try {
                for (int i = 0; i < rect.height; i++) {
                    for (int j = 0; j < rect.width; j++) {
                        int temp = i * yOffset + j + rect.x;
                        tile[(i + yinit) * width + j + xinit] = preloadedData[temp] & 0xFFFF;
                    }
                }
            } catch (Exception e) {
                System.out.println(e);
            }
        }
        return tile;
    }
    /*
    @Override
    public int[] readAndDecimateTile(int x, int y, int width, int height, int outWidth, int outHeight, boolean filter) {
    if(x<0||y<0||x+width>xSize||y+height>ySize){
    return super.readAndDecimateTile(x, y, width, height, outWidth, outHeight, false);
    }
    stride[1] = width/outWidth;
    stride[0] = height / outHeight;
    dims[1] = outWidth;
    dims[0] = outHeight;
    starts[1] = x;
    starts[0] = y;
    try {
    short[] o2 = (short[]) imagedata.read();
    int[] out=new int[o2.length];
    for(int i=0;i<out.length;i++){
    int temp1=o2[i];
    int temp2=(byte)(temp1>> 8);
    temp1=(byte)temp1;
    out[i]=(int)Math.sqrt(temp1*temp1+temp2*temp2);
    }
    return out;
    } catch (Exception ex) {
    Logger.getLogger(CosmoSkymedImage.class.getName()).log(Level.SEVERE, null, ex);
    return null;
    }
    }

     */
    /*
    @Override
    public int[] readAndDecimateTile(int x, int y, int width, int height, double scalingFactor, boolean filter, IProgress progressbar) {
    stride[1] = (long) (1 / scalingFactor);
    stride[0] = (long) (1 / scalingFactor);
    dims[1] = (long) (width * scalingFactor);
    dims[0] = (long) (height * scalingFactor);
    starts[1] = x;
    starts[0] = y;
    try {
    short[] o2 = (short[]) imagedata.read();
    int[] out=new int[o2.length];
    for(int i=0;i<out.length;i++){
    out[i]=o2[i];
    }
    return out;
    } catch (Exception ex) {
    Logger.getLogger(CosmoSkymedImage.class.getName()).log(Level.SEVERE, null, ex);
    return null;
    }
    }
     */

    public int read(int x, int y) {
        if (x < 0 || y < 0 || x > xSize || y > ySize) {
            return -1;
        }
        stride[1] = 1;
        stride[0] = 1;
        dims[1] = 1;
        dims[0] = 1;
        starts[1] = x;
        starts[0] = y;
        try {
            Object o2 = imagedata.read();
            return Array.getInt(o2, 0) & 0xFFFF;
        } catch (HDF5Exception ex) {
            Logger.getLogger(CosmoSkymedImage.class.getName()).log(Level.SEVERE, null, ex);
            return 0;
        }
    }

    public String getBandName(int band) {
        return "0";
    }

    public void setBand(int band) {
    }

    public void preloadLineTile(int y, int height) {
        if (y < 0) {
            return;
        }
        preloadedInterval = new int[]{y, y + height};
        stride[1] = 1;
        stride[0] = 1;
        dims[1] = xSize;
        dims[0] = height;
        starts[1] = 0;
        starts[0] = y;
        try {
            preloadedData = (short[]) imagedata.read();
        } catch (Exception ex) {
            Logger.getLogger(CosmoSkymedImage.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * For convenience the class try to extract the quiclook from the hdf5 file
     * and save it in the image folder for easier access as "preview.jpg"
     */
    private void extractQuickLook() {
        String overview = h5file.getParentFile().getAbsolutePath() + File.separator + "preview.jpg";
        if (new File(overview).exists()) {
            return;
        }
        try {
            H5ScalarDS ql = (H5ScalarDS) h5file.get("S01/QLK");
            if (ql == null) {
                ql = (H5ScalarDS) h5file.get("QLK");
                if (ql == null) {
                    return;
                }
            }

            Object data = ql.getData();
            BufferedImage bi = new BufferedImage(ql.getWidth(), ql.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
            bi.getRaster().setDataElements(0, 0, bi.getWidth(), bi.getHeight(), data);
            ImageIO.write(bi, "jpg", new File(overview));
        } catch (Exception ex) {
            Logger.getLogger(CosmoSkymedImage.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
